package com.yjzx.util.common.util.map;

import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author yjzx
 * @date 2024/7/5
 * @description 带有过期时间的缓存map
 */
public class MyRefTimeMap<T> implements Map<Object, T> {
    private final MyRefMap<Node<T>> refMap;

    /**
     * 允许存在时间
     */
    private final long clock;

    public <U> Node<U> getNode(U value) {
        return new Node<>(clock, value);
    }

    public <U> Node<U> getNoTermNode(U value) {
        return new Node<>(clock, value);
    }

    /**
     * @param nullValue    空值添加默认值
     * @param numericTypes 同值同key（暂时只有数字）
     * @param clock        (时钟，指定过期时间，单位是秒)
     */
    public MyRefTimeMap(T nullValue, boolean numericTypes, long clock) {
        this.clock = clock;
        refMap = MyRefMap.getRef(()->getNoTermNode(nullValue), numericTypes);
    }

    public MyRefTimeMap(boolean numericTypes, long clock) {
        this(null, numericTypes, clock);
    }

    public boolean isRecycle() {
        return refMap.isRecycle();
    }

    /**
     * 添加当前对象的是否需要清理（使用完成后也可以清理）
     *
     * @param enable
     * @return
     */
    public MyRefTimeMap<T> setRecycle(boolean enable) {
        refMap.setRecycle(enable);
        return this;
    }

    public T put(Object key, T value, Class<? extends Reference<?>> clazz) {
        Node<T> node = refMap.put(key, getNode(value), clazz);
        return node != null ? node.getValue() : null;
    }

    @Override
    public T remove(Object key) {
        Node<T> node = refMap.remove(key);
        return node != null ? node.getValue() : null;
    }

    public void remove(Object... keys) {
        refMap.remove(keys);
    }

    public void removeAll() {
        refMap.removeAll();
    }

    @Override
    public T get(Object key) {
        Node<T> node = refMap.get(key);
        return node != null ? node.getValue() : null;
    }

    public T put(Object key, Reference<Node<T>> value) {
        Node<T> node = clock <= 0 ? refMap.put(key, (Reference<Node<T>>) null) : refMap.put(key, value);
        return node != null ? node.getValue() : null;
    }

    public T putSoft(Object key, T value) {
        Node<T> node = refMap.putSoft(key, getNode(value));
        return node != null ? node.getValue() : null;
    }

    public T putWeak(Object key, T value) {
        Node<T> node = refMap.putWeak(key, getNode(value));
        return node != null ? node.getValue() : null;
    }

    @Override
    public int size() {
        return refMap.size();
    }

    @Override
    public boolean isEmpty() {
        return refMap.isEmpty();
    }

    @Override
    public boolean containsKey(Object key) {
        return refMap.containsKey(key);
    }

    @Override
    public boolean containsValue(Object value) {
        return value instanceof Node && refMap.containsValue(value);
    }

    @Override
    public T put(Object key, T value) {
        Node<T> node = clock <= 0 ? refMap.put(key, (Reference<Node<T>>) null) : refMap.put(key, getNode(value));
        return node != null ? node.getValue() : null;
    }

    @Override
    public void putAll(Map<?, ? extends T> map) {
        map.forEach(this::put);
    }

    @Override
    public void clear() {
        refMap.clear();
    }

    @Override
    public Set<Object> keySet() {
        return refMap.keySet();
    }

    @Override
    public Collection<T> values() {
        return new ValuesView<>(this);
    }

    @Override
    public Set<Map.Entry<Object, T>> entrySet() {
        return new EntryView(this);
    }

    @Override
    public boolean equals(Object o) {
        return o instanceof MyRefTimeMap && ((MyRefTimeMap<?>) o).clock == this.clock && refMap.equals(o);
    }

    @Override
    public int hashCode() {
        return refMap.hashCode();
    }

    static final class ValuesView<T> extends CollectionView<Object, Reference<Node<T>>, T> {
        private final MyRefTimeMap<T> map;

        ValuesView(MyRefTimeMap<T> map) {
            super(map.refMap.toRefMap());
            this.map = map;
        }

        @Override
        public Iterator<T> iterator() {
            return new ValuesIterator<>(map.refMap.toRefMap());
        }

        @Override
        public boolean add(Object o) {
            throw new UnsupportedOperationException();
        }

        @Override
        public boolean contains(Object o) {
            return map.containsValue(o);
        }

        @Override
        public boolean remove(Object o) {
            if (o != null) {
                for (Iterator<T> it = iterator(); it.hasNext(); ) {
                    if (o.equals(it.next())) {
                        it.remove();
                        return true;
                    }
                }
            }
            return false;
        }

        @Override
        public boolean addAll(Collection c) {
            throw new UnsupportedOperationException();
        }

    }

    static final class EntryView<T> extends CollectionView<Object, Reference<Node<T>>, EntryNode<T>> implements Set<EntryNode<T>> {
        private final ConcurrentHashMap<Object, Reference<Node<T>>> map;
        private final long clock;

        EntryView(MyRefTimeMap<T> map) {
            super(map.refMap.toRefMap());
            MyRefMap<Node<T>> refMap = map.refMap;
            this.map = refMap.toRefMap();
            this.clock = map.clock;
        }

        @Override
        public Iterator<EntryNode<T>> iterator() {
            return new EntryIterator<>(map);
        }

        @Override
        public boolean add(EntryNode<T> objectTEntry) {
            return map.put(objectTEntry.getKey(), new WeakReference<>(new Node<>(clock, objectTEntry.getValue()))) == null;
        }

        @Override
        public boolean contains(Object o) {
            return map.contains(o);
        }

        @Override
        public boolean remove(Object o) {
            if (o != null) {
                for (Iterator<EntryNode<T>> it = iterator(); it.hasNext(); ) {
                    if (o.equals(it.next())) {
                        it.remove();
                        return true;
                    }
                }
            }
            return false;
        }

        @Override
        public boolean addAll(Collection<? extends EntryNode<T>> c) {
            boolean added = false;
            for (EntryNode<T> e : c) {
                if (add(e)) {
                    added = true;
                }
            }
            return added;
        }

    }

    static final class ValuesIterator<T> implements Iterator<T> {
        private final Iterator<Reference<Node<T>>> iterator;
        private T nextEntry;

        ValuesIterator(Map<Object, Reference<Node<T>>> map) {
            this.iterator = map.values().iterator();
        }

        @Override
        public boolean hasNext() {
            Reference<Node<T>> entry;
            Node<T> value;
            T t;
            while (iterator.hasNext()) {
                entry = iterator.next();
                value = entry.get();
                if (value != null && (t = value.getValue()) != null) {
                    nextEntry = t;
                    return true;
                } else {
                    // 如果引用已经被回收，移除该条目
                    iterator.remove();
                }
            }
            return false;
        }

        @Override
        public T next() {
            if (nextEntry == null && !hasNext()) {
                throw new java.util.NoSuchElementException();
            }
            T result = nextEntry;
            nextEntry = null;
            return result;
        }

        @Override
        public void remove() {
            if (nextEntry == null) {
                throw new IllegalStateException();
            }
            iterator.remove();
            nextEntry = null;
        }
    }

    static final class EntryIterator<T> implements Iterator<EntryNode<T>> {
        private final Iterator<Map.Entry<Object, Reference<Node<T>>>> iterator;
        private EntryNode<T> nextEntry;

        EntryIterator(Map<Object, Reference<Node<T>>> map) {
            this.iterator = map.entrySet().iterator();
        }

        @Override
        public boolean hasNext() {
            Entry<Object, Reference<Node<T>>> entry;
            Node<T> value;
            while (iterator.hasNext()) {
                entry = iterator.next();
                value = entry.getValue().get();
                if (value != null && value.getValue() != null) {
                    nextEntry = new EntryNode<>(entry);
                    return true;
                } else {
                    // 如果引用已经被回收，移除该条目
                    iterator.remove();
                }
            }
            return false;
        }

        @Override
        public EntryNode<T> next() {
            if (nextEntry == null && !hasNext()) {
                throw new java.util.NoSuchElementException();
            }
            EntryNode<T> result = nextEntry;
            nextEntry = null;
            return result;
        }

        @Override
        public void remove() {
            if (nextEntry == null) {
                throw new IllegalStateException();
            }
            iterator.remove();
            nextEntry = null;
        }
    }

    abstract static class CollectionView<K, V, E> implements Collection<E>, java.io.Serializable {
        private static final long serialVersionUID = 7249069246763182397L;
        private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
        final ConcurrentHashMap<K, V> map;

        CollectionView(ConcurrentHashMap<K, V> map) {
            this.map = map;
        }

        public ConcurrentHashMap<K, V> getMap() {
            return map;
        }

        /**
         * Removes all of the elements from this view, by removing all
         * the mappings from the map backing this view.
         */
        @Override
        public final void clear() {
            map.clear();
        }

        @Override
        public final int size() {
            return map.size();
        }

        @Override
        public final boolean isEmpty() {
            return map.isEmpty();
        }

        // implementations below rely on concrete classes supplying these
        // abstract methods

        /**
         * Returns an iterator over the elements in this collection.
         *
         * <p>The returned iterator is
         * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
         *
         * @return an iterator over the elements in this collection
         */
        @Override
        public abstract Iterator<E> iterator();

        @Override
        public abstract boolean contains(Object o);

        @Override
        public abstract boolean remove(Object o);

        private static final String oomeMsg = "Required array size too large";

        @Override
        public final Object[] toArray() {
            long sz = map.mappingCount();
            if (sz > MAX_ARRAY_SIZE) {
                throw new OutOfMemoryError(oomeMsg);
            }
            int n = (int) sz;
            Object[] r = new Object[n];
            int i = 0;
            for (E e : this) {
                if (i == n) {
                    if (n >= MAX_ARRAY_SIZE) {
                        throw new OutOfMemoryError(oomeMsg);
                    }
                    if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) {
                        n = MAX_ARRAY_SIZE;
                    } else {
                        n += (n >>> 1) + 1;
                    }
                    r = Arrays.copyOf(r, n);
                }
                r[i++] = e;
            }
            return (i == n) ? r : Arrays.copyOf(r, i);
        }

        @Override
        @SuppressWarnings("unchecked")
        public final <T1> T1[] toArray(T1[] array) {
            long sz = map.mappingCount();
            if (sz > MAX_ARRAY_SIZE) {
                throw new OutOfMemoryError(oomeMsg);
            }
            int m = (int) sz;
            T1[] r = (array.length >= m) ? array :
                    (T1[]) java.lang.reflect.Array
                            .newInstance(array.getClass().getComponentType(), m);
            int n = r.length;
            int i = 0;
            for (E e : this) {
                if (i == n) {
                    if (n >= MAX_ARRAY_SIZE) {
                        throw new OutOfMemoryError(oomeMsg);
                    }
                    if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) {
                        n = MAX_ARRAY_SIZE;
                    } else {
                        n += (n >>> 1) + 1;
                    }
                    r = Arrays.copyOf(r, n);
                }
                r[i++] = (T1) e;
            }
            if (array == r && i < n) {
                r[i] = null; // null-terminate
                return r;
            }
            return (i == n) ? r : Arrays.copyOf(r, i);
        }

        /**
         * Returns a string representation of this collection.
         * The string representation consists of the string representations
         * of the collection's elements in the order they are returned by
         * its iterator, enclosed in square brackets ({@code "[]"}).
         * Adjacent elements are separated by the characters {@code ", "}
         * (comma and space).  Elements are converted to strings as by
         * {@link String#valueOf(Object)}.
         *
         * @return a string representation of this collection
         */
        @Override
        public final String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append('[');
            Iterator<E> it = iterator();
            if (it.hasNext()) {
                for (; ; ) {
                    Object e = it.next();
                    sb.append(e == this ? "(this Collection)" : e);
                    if (!it.hasNext()) {
                        break;
                    }
                    sb.append(',').append(' ');
                }
            }
            return sb.append(']').toString();
        }

        @Override
        public final boolean containsAll(Collection<?> c) {
            if (c != this) {
                for (Object e : c) {
                    if (e == null || !contains(e)) {
                        return false;
                    }
                }
            }
            return true;
        }

        @Override
        public final boolean removeAll(Collection<?> c) {
            if (c == null) {
                throw new NullPointerException();
            }
            boolean modified = false;
            for (Iterator<E> it = iterator(); it.hasNext(); ) {
                if (c.contains(it.next())) {
                    it.remove();
                    modified = true;
                }
            }
            return modified;
        }

        @Override
        public final boolean retainAll(Collection<?> c) {
            if (c == null) {
                throw new NullPointerException();
            }
            boolean modified = false;
            for (Iterator<E> it = iterator(); it.hasNext(); ) {
                if (!c.contains(it.next())) {
                    it.remove();
                    modified = true;
                }
            }
            return modified;
        }

    }

    static class EntryNode<T> implements MyEntry<Object, T> {
        private final Map.Entry<Object, Reference<Node<T>>> entry;

        public EntryNode(Map.Entry<Object, Reference<Node<T>>> entry) {
            this.entry = entry;
        }

        @Override
        public Entry getEntry() {
            return entry;
        }

        @Override
        public Object getKey() {
            return entry.getKey();
        }

        Node<T> getValueNode() {
            if (entry != null && entry.getValue() != null && entry.getValue().get() != null) {
                return entry.getValue().get();
            }
            return null;
        }

        @Override
        public T getValue() {
            Node<T> node = getValueNode();
            return node != null ? node.getValue() : null;
        }

        @Override
        public T setValue(T value) {
            entry.setValue(new WeakReference<>(new Node<>(getValueNode().getClock(), value)));
            return value;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            EntryNode entryNode = (EntryNode) o;

            return entry != null ? entry.equals(entryNode.entry) : entryNode.entry == null;
        }

        @Override
        public int hashCode() {
            return entry != null ? entry.hashCode() : 0;
        }

        @Override
        public String toString() {
            return "EntryNode{" +
                    "key=" + getKey() +
                    ",value=" + getValue() +
                    '}';
        }
    }

}
